package com.atguigu.gmall.activity.listener;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.utils.CacheHelper;
import com.atguigu.gmall.activity.utils.DateUtil;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.atguigu.gmall.rabbit.constants.MQConst;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * @Author: lx
 * @Time: 15:01
 * @Description: 从DB压入 数据到缓存中
 */
@Component
public class DBUpdateToRedis {

    @Autowired
    private SeckillGoodsService seckillGoodsService;
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    //MQ消费方
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MQConst.QUEUE_TASK_1, durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_TASK),
            key = MQConst.ROUTING_TASK_1
    ))
    public void toRedis(Message message, Channel channel) {
        try {
            //此时正好半夜0点
            //1: 查询DB中数据
      /*      select * from seckill_goods
            where  status = 1 and  stock_count > 0
            and DATE_FORMAT(start_time,'%Y-%m-%d') = '2020-12-08'*/
            QueryWrapper<SeckillGoods> wrapper = new QueryWrapper();
            wrapper.eq("status", "1");
            wrapper.eq("DATE_FORMAT(start_time,'%Y-%m-%d')",
                    DateUtil.formatDate(new Date()));
            wrapper.gt("stock_count", 0);
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(wrapper);
            if (!CollectionUtils.isEmpty(seckillGoodsList)) {
                seckillGoodsList.forEach(seckillGoods -> {
                    //2:保存到Redis  Map seckillGoodsList  seckillGoodsList.put(skuId,SeckillGoods)
                    if (!redisTemplate.opsForHash().hasKey(
                            RedisConst.SECKILL_GOODS, seckillGoods.getSkuId().toString())) {
                        //在不存在 的情况下 添加
                        redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS
                                , seckillGoods.getSkuId().toString(), seckillGoods);
                        //3：防止超卖 （秒杀核心内容）  List类型 防止超卖
                        redisTemplate.opsForList().leftPushAll(
                                RedisConst.SECKILL_STOCK_PREFIX + seckillGoods.getSkuId(),
                                buildSkuIdList(seckillGoods)
                        );
                        //4: 将 skuId:1 某款商品的库存状态 放入各大秒杀微服务的内存中
                        //群发   MQ  队列  大家监听一个队列是竞争关系
                        //群发 Redis  发布订阅   发消息  同一个队列  被N多个消费者所消费 不是竞争关系
                        redisTemplate.convertAndSend("seckillpush",
                                seckillGoods.getSkuId() + ":1");
                    }

                });
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            //e.printStackTrace();
        }
    }

    //构建库存IDS
    private String[] buildSkuIdList(SeckillGoods seckillGoods) {
        String[] ids = new String[seckillGoods.getStockCount()];
        for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
            ids[i] = seckillGoods.getSkuId().toString();
        }
        return ids;
    }
    // 四个： 值类型  Hash类型（散列类型） List列表（列表类型） Set类型（集合类型）
    //  ZSset类型（有序集合类型）  热点数据


    //开始秒杀
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MQConst.QUEUE_SECKILL_USER, durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = MQConst.ROUTING_SECKILL_USER
    ))
    public void seckillGoods(UserRecode userRecode,Message message, Channel channel) {
        try {
//            1、校验是否有库存（内存）
            Object state = CacheHelper.get(userRecode.getSkuId().toString());
            if(null == state || !state.equals("1")){
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            2、校验用户是否已经购买过了
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(RedisConst.SECKILL_USER +
                    userRecode.getUserId(), "");
            if(!ifAbsent){
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            3、校验rightpop  Redis   更新内存中库
            Object rightPop = redisTemplate.opsForList().rightPop(RedisConst.SECKILL_STOCK_PREFIX +
                    userRecode.getSkuId());
            if(null == rightPop){
                //已售完
                redisTemplate.convertAndSend("seckillpush",
                        userRecode.getSkuId() + ":0");
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            4、在Redis中保存抢购资格
            OrderRecode orderRecode = new OrderRecode();//下单资格  抢购成功
            redisTemplate.opsForValue().set(RedisConst.SECKILL_ORDERS +
                    userRecode.getUserId(),orderRecode);
            //5:后续处理  （不写了）
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch(Exception e){
            //e.printStackTrace();
        }

    }
}
